//***************************************************************************
//*
//*	File:			DialogProjects.cpp
//*	Description:	Dialog for managing projects
//*
//***************************************************************************

//
//--------------------------------------------------------------- PRECOMPILED
//

#include "stdafx.h"

//
//--------------------------------------------------- DECLARATION DEPENDENCIES
//

#include "BuildMgr2.h"

//
//--------------------------------------------------------------- DECLARATION
//

#include "DialogProjects.h"

//
//--------------------------------------------------- DEFINITION DEPENDENCIES
//

//
//-------------------------------------------------------------- PREPROCESSOR
//

#ifdef	_DEBUG
#define new DEBUG_NEW
#undef	THIS_FILE
static	char THIS_FILE[] = __FILE__;
#endif

//
//---------------------------------------------------------------- DEFINITION
//

//
//---------------------------------------------------------------------------------------------------
//*************************************     CON/DESTRUCTION     *************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																					  Constructor
//***************************************************************************************************
//**	@DOC		CONSTRUCTION
//**	@MFUNC		Default constructor
//**	@PARM		[in|opt] Pointer to the parent window
//**	@END
//***************************************************************************************************
//inline
CDialogProjects::CDialogProjects( CWnd* pParent /*=NULL*/ )
: CDialog( CDialogProjects::IDD, pParent )
{
	//{{AFX_DATA_INIT(CDialogProjects)
	//}}AFX_DATA_INIT

	this->m_pIApplication = NULL;
}

//
//---------------------------------------------------------------------------------------------------
//*****************************************     GET/SET     *****************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																				   SetApplication
//***************************************************************************************************
//**	@DOC		SET
//**	@MFUNC		Sets the pointer to the application's interface
//**	@PARM		[in] Pointer to application's interface
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::SetApplication(IApplication *a_pIApplication)
{
	this->m_pIApplication = a_pIApplication;
}

//
//---------------------------------------------------------------------------------------------------
//***************************************     PERSISTENCE     ***************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																					  LoadProfile 
//***************************************************************************************************
//**	@DOC		PERSISTENCE
//**	@MFUNC		Loads the dialog's settings from the registry
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::LoadProfile( void )
{
	//
	//	RESET LIST
	//
	this->m_ctrlListWorkspaces.DeleteAllItems();

	//
	//	SETUP VARS
	//
	CString strWorkspacePath	;
	CString strWorkspaceName	;
	CString strProjectPath		;
	CString strProjectName		;
	CString	strConfigurations	;
	CString strSection			;
	UINT	iProjects			= 0;
	UINT	iStyle				= 0;
	DWORD	dwCount				= 0;

	///
	//	GET APP
	//
	CWinApp* pApplication =	::AfxGetApp();

	//
	//	GET SECTION KEY
	//
	CRegKey	keySection;	
			keySection.Attach( pApplication->GetSectionKey( _T( "Configure" ) ) );

	//
	//	READ PROJECT COUNT
	//
	iProjects = pApplication->GetProfileInt( _T( "Configure" ), _T( "NumProjects" ), 0 );

	//
	//	READ PROJECTS
	//
	for ( UINT iProject = 0; iProject < iProjects; iProject++ )
	{
		//
		//	SETUP SECTION
		//
		strSection.Format( "Project.%04d", iProject );

		//
		//	GET SUB KEY
		//
		CRegKey keyProject	;
				keyProject.Open( keySection, strSection );

		//
		//	READ PROJECT
		//
		dwCount = _MAX_PATH; keyProject.QueryValue( strProjectName		.GetBuffer( _MAX_PATH )	, _T( "0.Project.Name"		), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( strProjectPath		.GetBuffer( _MAX_PATH )	, _T( "1.Project.Path"		), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( strWorkspaceName	.GetBuffer( _MAX_PATH )	, _T( "2.Workspace.Name"	), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( strWorkspacePath	.GetBuffer( _MAX_PATH )	, _T( "3.Workspace.Path"	), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( strConfigurations	.GetBuffer( _MAX_PATH )	, _T( "4.Configurations"	), &dwCount );	
		dwCount = _MAX_PATH; keyProject.QueryValue( (DWORD&) iStyle								, _T( "5.Style"				) ); 

		strProjectName		.ReleaseBuffer();
		strProjectPath		.ReleaseBuffer();
		strWorkspaceName	.ReleaseBuffer();
		strWorkspacePath	.ReleaseBuffer();
		strConfigurations	.ReleaseBuffer();
		
		//
		//	CLOSE THE KEY
		//
		keyProject.Close();

		//
		//	ADD PROJECT
		//
		this->AddProject( strWorkspacePath, strWorkspaceName, strProjectPath, strProjectName, strConfigurations, iStyle );
	}

	//
	//	CLOSE MAIN SECTION KEY
	//
	keySection.Close ();
	keySection.Detach();
}

//***************************************************************************************************
//**																					 WriteProfile 
//***************************************************************************************************
//**	@DOC		PERSISTENCE
//**	@MFUNC		Writes the dialog's settings to the registry
//**	@RDESC		<t TRUE> if successful, <t FALSE> otherwise
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogProjects::WriteProfile( void ) const
{
	//
	//	SETUP VARS
	//
	CString strWorkspacePath	;
	CString strWorkspaceName	;
	CString strProjectPath		;
	CString strProjectName		;
	CString	strConfigurations	;
	CString strSection			;
	int		iProjects			= this->m_ctrlListWorkspaces.GetItemCount();
	int		iProject			= 0;
	int		iStyle				= 0;

	///
	//	GET APP
	//
	CWinApp* pApplication =	::AfxGetApp();

	//
	//	DELETE SECTION KEY
	//
/*	CRegKey	keyApp	;
			keyApp	.Attach			 ( pApplication->GetAppRegistryKey() );
			keyApp	.RecurseDeleteKey( _T( "Configure" ) );
			keyApp	.Close			 ();
			keyApp	.Detach			 ();
*/

	// @FIXME: this would also delete the main configuration's settings (email etc.)

	//
	//	CREATE SECTION KEY
	//
	CRegKey	keySection;	
			keySection.Attach( pApplication->GetSectionKey( _T( "Configure" ) ) );

	//
	//	WRITE PROJECT COUNT
	//
	pApplication->WriteProfileInt( _T( "Configure" ), _T( "NumProjects" ), this->m_ctrlListWorkspaces.GetItemCount() );

	//
	//	WRITE PROJECTS
	//
	for ( iProject = 0; iProject < iProjects; iProject++ )
	{
		//
		//	SETUP SECTION
		//
		strSection.Format( "Project.%04d", iProject );

		//
		//	CREATE SUB KEY
		//
		CRegKey keyProject	;
				keyProject.Create( keySection, strSection );

		//
		//	GET PROJECT
		//
		this->GetProject( iProject, strWorkspacePath, strWorkspaceName, strProjectPath, strProjectName, strConfigurations, iStyle );

		//
		//	WRITE PROJECT
		//
		keyProject.SetValue( strProjectName		, _T( "0.Project.Name"		) );
		keyProject.SetValue( strProjectPath		, _T( "1.Project.Path"		) );
		keyProject.SetValue( strWorkspaceName	, _T( "2.Workspace.Name"	) );
		keyProject.SetValue( strWorkspacePath	, _T( "3.Workspace.Path"	) );
		keyProject.SetValue( strConfigurations	, _T( "4.Configurations"	) );
		keyProject.SetValue( iStyle				, _T( "5.Style"				) ); 

		//
		//	CLOSE THE KEY
		//
		keyProject.Close();
	}

	//
	//	CLOSE MAIN SECTION KEY
	//
	keySection.Close ();
	keySection.Detach();

	//
	//	RETURN SUCCESS
	//
	return TRUE;
}

//***************************************************************************************************
//**																					 ImportBuilds							  
//***************************************************************************************************
//**	@DOC		PERSISTENCE
//**	@MFUNC		Imports the projects from the specified file
//**	@PARM		[in] The path of the file to import projects from
//**	@RDESC		<t TRUE> if successful, <t FALSE> otherwise.
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogProjects::ImportBuilds( const CString &a_strPath )
{
	//
	//	RESET LIST
	//
	this->m_ctrlListWorkspaces.DeleteAllItems();

	//
	//	SETUP VARS
	//
	CString strWorkspacePath	;
	CString strWorkspaceName	;
	CString strProjectPath		;
	CString strProjectName		;
	CString	strConfigurations	;
	int		iProjects			= 0;
	int		iStyle				= 0;

	//
	//	OPEN FILE
	//
	CStdioFile fileIn( a_strPath, CFile::modeRead | CFile::typeText );

	//
	//	READ PROJECT COUNT
	//
	fileIn.Read( &iProjects, sizeof( int ) );

	//
	//	READ PROJECTS
	//
	for ( int iProject = 0; iProject < iProjects; iProject++ )
	{
		//
		//	READ PROJECT
		//
		fileIn.ReadString( strProjectName		);
		fileIn.ReadString( strWorkspaceName		);
		fileIn.ReadString( strProjectPath		);
		fileIn.ReadString( strWorkspacePath		);
		fileIn.ReadString( strConfigurations	);
		fileIn.Read		 ( &iStyle, sizeof( int ) );

		//
		//	CORRECT CONFIGS
		//
		if ( ";" == strConfigurations.Right( 1 ) )
		{
			strConfigurations = strConfigurations.Left( strConfigurations.GetLength() - 1 );
		}

		//
		//	ADD PROJECT
		//
		this->AddProject( strWorkspacePath, strWorkspaceName, strProjectPath, strProjectName, strConfigurations, iStyle );
	}

	//
	//	CLOSE FILE
	//
	fileIn.Close();

	//
	//	RETURN SUCCESS
	//
	return TRUE;
}

//***************************************************************************************************
//**																					 ExportBuilds
//***************************************************************************************************
//**	@DOC		PERSISTENCE
//**	@MFUNC		Exports the projects to the specified file
//**	@PARM		[in] The path of the file to export projects to
//**	@RDESC		<t TRUE> if successful, <t FALSE> otherwise.
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogProjects::ExportBuilds( const CString &a_strPath ) const
{
	//
	//	SETUP VARS
	//
	CString strWorkspacePath	;
	CString strWorkspaceName	;
	CString strProjectPath		;
	CString strProjectName		;
	CString	strConfigurations	;
	int		iProjects			= this->m_ctrlListWorkspaces.GetItemCount();
	int		iStyle				= 0;

	//
	//	OPEN FILE
	//
	CStdioFile fileOut( a_strPath, CFile::modeCreate | CFile::modeWrite | CFile::typeText );

	//
	//	WRITE PROJECT COUNT
	//
	fileOut.Write( &iProjects, sizeof( int ) );

	//
	//	WRITE PROJECTS
	//
	for ( int iProject = 0; iProject < iProjects; iProject++ )
	{
		//
		//	GET PROJECT
		//
		this->GetProject( iProject, strWorkspacePath, strWorkspaceName, strProjectPath, strProjectName, strConfigurations, iStyle );

		//
		//	WRITE PROJECT
		//
		fileOut.WriteString	( strProjectName	+ "\n" );
		fileOut.WriteString	( strWorkspaceName	+ "\n" );
		fileOut.WriteString	( strProjectPath	+ "\n" );
		fileOut.WriteString	( strWorkspacePath	+ "\n" );
		fileOut.WriteString	( strConfigurations	+ "\n" );
		fileOut.Write		( &iStyle,sizeof( int ) );
	}

	//
	//	CLOSE FILE
	//
	fileOut.Close();

	//
	//	RETURN SUCCESS
	//
	return TRUE;
}

//
//---------------------------------------------------------------------------------------------------
//****************************************     VIRTUALS     *****************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																				   DoDataExchange
//***************************************************************************************************
//**	@DOC		VIRTUALS
//**	@MFUNC		Exchanges data between the dialog's members and its controls
//**	@PARM		[in/out] Pointer to data exchange context
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange( pDX );

	//{{AFX_DATA_MAP(CDialogProjects)
	DDX_Control( pDX, IDC_WORKSPACELIST, m_ctrlListWorkspaces );
	//}}AFX_DATA_MAP
}

//
//---------------------------------------------------------------------------------------------------
//****************************************     MESSAGES     *****************************************
//---------------------------------------------------------------------------------------------------
//

BEGIN_MESSAGE_MAP( CDialogProjects, CDialog )
	//{{AFX_MSG_MAP(CDialogProjects)
	ON_BN_CLICKED		(			IDC_ADD_PROJECT		, OnAddProject			)
	ON_BN_CLICKED		(			IDC_IMPORT			, OnImport				)
	ON_BN_CLICKED		(			IDC_EXPORT			, OnExport				)
	ON_BN_CLICKED		(			IDC_REMOVE_PROJECT	, OnRemoveProject		)
	ON_BN_CLICKED		(			IDC_MOVE_UP			, OnMoveUp				)
	ON_BN_CLICKED		(			IDC_MOVE_DOWN		, OnMoveDown			)
	ON_NOTIFY			( NM_CLICK, IDC_WORKSPACELIST	, OnClickWorkspaceList	)
	ON_WM_SIZE			()
	ON_WM_GETMINMAXINFO	()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

BEGIN_EASYSIZE_MAP( CDialogProjects )
	//			CONTROL				LEFT			TOP				RIGHT		BOTTOM	OPTIONS
    EASYSIZE( IDOK				, ES_KEEPSIZE	, ES_BORDER		, ES_BORDER	, ES_KEEPSIZE	, 0 )
    EASYSIZE( IDCANCEL			, ES_KEEPSIZE	, ES_BORDER		, ES_BORDER	, ES_KEEPSIZE	, 0 )
    EASYSIZE( IDC_ADD_PROJECT	, ES_KEEPSIZE	, ES_BORDER		, ES_BORDER	, ES_KEEPSIZE	, 0 )
    EASYSIZE( IDC_REMOVE_PROJECT, ES_KEEPSIZE	, ES_BORDER		, ES_BORDER	, ES_KEEPSIZE	, 0 )
    EASYSIZE( IDC_MOVE_UP		, ES_KEEPSIZE	, ES_BORDER		, ES_BORDER	, ES_KEEPSIZE	, 0 )
    EASYSIZE( IDC_MOVE_DOWN		, ES_KEEPSIZE	, ES_BORDER		, ES_BORDER	, ES_KEEPSIZE	, 0 )
    EASYSIZE( IDC_IMPORT		, ES_KEEPSIZE	, ES_BORDER		, ES_BORDER	, ES_KEEPSIZE	, 0 )
    EASYSIZE( IDC_EXPORT		, ES_KEEPSIZE	, ES_BORDER		, ES_BORDER	, ES_KEEPSIZE	, 0 )
    EASYSIZE( IDC_WORKSPACELIST	, ES_BORDER		, IDOK			, IDOK		, ES_BORDER		, 0 )
END_EASYSIZE_MAP

//***************************************************************************************************
//**																					 OnInitDialog
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called to initialize the dialog
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogProjects::OnInitDialog() 
{
	//
	//	CALL BASE CLASS' IMPLEMENTATION
	//
	CDialog::OnInitDialog();

	//
	//	INIT EASY SIZE
	//
	INIT_EASYSIZE;

	//
	//	CREATE IMAGE LIST
	//
	HICON hIcon = ::AfxGetApp()->LoadIcon( IDI_PROJECT );

	this->m_ImageList.Create(16, 16, ILC_COLOR, 1, 1);
	this->m_ImageList.SetBkColor(::GetSysColor(COLOR_WINDOW));
	this->m_ImageList.Add(hIcon);

	//
	//	ATTACH IMAGE LIST OT LIST CONTROL
	//
	this->m_ctrlListWorkspaces.SetImageList(&m_ImageList, LVSIL_SMALL);

	//
	//	SETUP LIST CONTROL'S COLUMNS
	//
	CString strHeader;
	int		iColumn	 ;

	for ( UINT idColumn = IDS_COL_PRJ_MIN; idColumn <= IDS_COL_PRJ_MAX; idColumn++ ) 
	{
		strHeader.LoadString( idColumn );

		iColumn = idColumn - IDS_COL_PRJ_MIN;

		this->m_ctrlListWorkspaces.InsertColumn( iColumn, strHeader, LVCFMT_LEFT, 150, iColumn );
	}
	
	//
	//	MODIFY STYLE
	//
	this->m_ctrlListWorkspaces.SetExtendedStyle( LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES );

	//
	//	LOAD PROFILE
	//
	this->LoadProfile();

	//
	//	SET THE FOCUS TO THE LIST CONTROL (neccessary in order to have the control re-display its selection)
	//
	this->m_ctrlListWorkspaces.SetFocus();

	//
	//	DO NOT SET AUTO-FOCUS
	//
	return FALSE;  	              
}

//***************************************************************************************************
//**																						   OnSize
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the dialog's size has changed
//**	@PARM		[in] The type of sizing that has occured
//**	@PARM		[in] The offset along the x axis
//**	@PARM		[in] The offset along the y axis
//**	@COMM		Updates controls' sizes
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::OnSize( UINT nType, int cx, int cy ) 
{
	//
	//	CALL BASE CLASS' IMPLEMENTATION
	//
	CDialog::OnSize(nType, cx, cy);

	//
	//	UPDATE EASYSIZE
	//
	UPDATE_EASYSIZE;
}

//***************************************************************************************************
//**																				  OnGetMinMaxInfo
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called to determine the dialog's min/max size
//**	@PARM		[in] Min/max info
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::OnGetMinMaxInfo( MINMAXINFO FAR* lpMMI ) 
{
	lpMMI->ptMinTrackSize.x = 400;
	lpMMI->ptMinTrackSize.y = 280;

	//
	//	CALL BASE CLASS' IMPLEMENTATION
	//
	CDialog ::OnGetMinMaxInfo(lpMMI);
}

//***************************************************************************************************
//**																							 OnOK
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the OK button is pressed
//**	@COMM		Writes the profile and closes the dialog
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::OnOK() 
{
	//
	//	SAVE PROFILE
	//
	this->WriteProfile();

	//
	//	CALL BASE CLASS' IMPLEMENTATION
	//
	CDialog::OnOK();
}

//***************************************************************************************************
//**																					 OnAddProject
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the Add button is pressed
//**	@COMM		Brings up the file dialog
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::OnAddProject() 
{
	//
	//	LOAD FILE SPECS
	//
	CString strFileSpec;
			strFileSpec.LoadString( IDS_PROJECTFILES );

	//
	//	LET USER SELECT PROJECTS
	//
	CFileDialog dlgBrowser( TRUE, "", "", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, strFileSpec );

	if ( IDOK != dlgBrowser.DoModal() )
	{
		return;
	}

	//
	//	ADD THE WORKSPACE
	//
	this->AddWorkspace( dlgBrowser.GetPathName(), dlgBrowser.GetFileTitle() );
}

//***************************************************************************************************
//**																				  OnRemoveProject
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the Remove button is pressed
//**	@COMM		Removes all selected projects
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::OnRemoveProject() 
{
	//
	//	SINCE WE GET ITEMS IN RANDOM ORDER WE HAVE TO RECURSE UNTIL NO MORE ITEM IS SELECTED
	//
	POSITION pos = this->m_ctrlListWorkspaces.GetFirstSelectedItemPosition();

	if ( NULL != pos )
	{
		//
		//	GET NEXT SELECTED ITEM'S INDEX
		//
		int iItem = this->m_ctrlListWorkspaces.GetNextSelectedItem( pos );

		//
		//	REMOVE THAT ITEM
		//
		this->m_ctrlListWorkspaces.DeleteItem( iItem );

		//
		//	RECURSE
		//
		this->OnRemoveProject();
	}
}

//***************************************************************************************************
//**																						 OnMoveUp
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the MoveUp button is pressed
//**	@COMM		Moves all selected projects one place up
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::OnMoveUp() 
{
	//
	//	SETUP VARS
	//
	int	iPredecessor = 0;
	int	iItem		 = 0;

	//
	//	ENUMERATE SELECTED ITEMS
	//
	POSITION pos = this->m_ctrlListWorkspaces.GetFirstSelectedItemPosition();

	while ( NULL != pos )
	{
		//
		//	GET NEXT SELECTED ITEM'S INDEX
		//
		iItem = this->m_ctrlListWorkspaces.GetNextSelectedItem( pos );

		//
		//	CHECK WHETHER WE CAN MOVE THAT ITEM
		//
		if ( iItem > iPredecessor )
		{
			//
			//	MOVE THAT ITEM
			//
			this->MoveItem( iItem, iItem - 1 );

			//
			//	KEEP ANY FOLLOWING ITEM FROM REPLACING THIS ITEM
			//
			iPredecessor = iItem;
		}
		else
		{
			//
			//	THIS ITEM COULD NOT BE MOVED
			//
			++iPredecessor;
		}
	}

	//
	//	ENSURE THAT OUR SELECTION IS VISIBLE
	//
	pos = this->m_ctrlListWorkspaces.GetFirstSelectedItemPosition();

	if ( NULL != pos )
	{
		//
		//	GET FIRST SELECTED ITEM'S POSITION
		//
		iItem = this->m_ctrlListWorkspaces.GetNextSelectedItem( pos );

		//
		//	ENSURE THAT ITEMS's VISIBILITY
		//
		this->m_ctrlListWorkspaces.EnsureVisible( iItem, FALSE );
	}

	//
	//	SET THE FOCUS BACK TO THE LIST CONTROL (neccessary in order to have the control re-display its selection)
	//
	this->m_ctrlListWorkspaces.SetFocus();
}

//***************************************************************************************************
//**																					   OnMoveDown
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the MoveDown button is pressed
//**	@COMM		Moves all selected projects one place down
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::OnMoveDown() 
{
	//
	//	GATHER SELECTED INDICES (for we must process them in reverse order)
	//
	CArray< int, int >	arrSelected	;
	POSITION			pos			= this->m_ctrlListWorkspaces.GetFirstSelectedItemPosition();

	if ( NULL == pos )
	{
		return;	// avoid messing around with GetUpperBound()
	}

	while ( NULL != pos )
	{
		//
		//	GET NEXT SELECTED ITEM'S INDEX
		//
		arrSelected.Add( this->m_ctrlListWorkspaces.GetNextSelectedItem( pos ) );
	}

	//
	//	NOW, PROCESS THE ITEMS
	//
	int iSuccessor  = this->m_ctrlListWorkspaces.GetItemCount() - 1;
	int iItem		= iSuccessor;
	int iSelected	= 0;

	for ( iSelected = arrSelected.GetUpperBound(); iSelected >= 0; iSelected-- )
	{
		//
		//	GET ITEM INDEX
		//
		iItem = arrSelected[ iSelected ];

		//
		//	CHECK WHETHER WE CAN MOVE THE ITEM
		//
		if ( iItem < iSuccessor )
		{
			//
			//	MOVE THAT ITEM
			//
			this->MoveItem( iItem, iItem + 1 );

			//
			//	PREVENT THAT ITEM FROM BEING OVERRIDEEN
			//
			iSuccessor = iItem;
		}
		else
		{
			//
			//	COULD NOT MOVE THAT ITEM
			//
			--iSuccessor;
		}
	}

	//
	//	ENSURE THAT OUR SELECTION IS VISIBLE
	//
	pos	= this->m_ctrlListWorkspaces.GetFirstSelectedItemPosition();

	while ( NULL != pos )
	{
		//
		//	GET LAST SELECTED ITEM'S POSITION
		//
		iItem = this->m_ctrlListWorkspaces.GetNextSelectedItem( pos );
	}

	//
	//	ENSURE THAT ITEMS's VISIBILITY
	//
	this->m_ctrlListWorkspaces.EnsureVisible( iItem, FALSE );

	//
	//	SET THE FOCUS BACK TO THE LIST CONTROL (neccessary in order to have the control re-display its selection)
	//
	this->m_ctrlListWorkspaces.SetFocus();
}

//***************************************************************************************************
//**																						 OnImport 
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the Import button is pressed
//**	@COMM		Brings up the file dialog and eventually calls <mf ImportBuilds>.
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::OnImport() 
{
	//
	//	LOAD FILE SPECS
	//
	CString strFileSpec;
			strFileSpec.LoadString( IDS_BUILDFILES );

	//
	//	LET USER SELECT BUILDS
	//
	CFileDialog dlgBrowser( TRUE, "", "", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, strFileSpec );

	if ( IDOK != dlgBrowser.DoModal() )
	{
		return;
	}

	//
	//	IMPORT
	//
	this->ImportBuilds( dlgBrowser.GetPathName() );
}

//***************************************************************************************************
//**																						 OnExport 
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the Export button is pressed
//**	@COMM		Brings up the file dialog and eventually calls <mf ExportBuilds>.
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::OnExport() 
{
	//
	//	LOAD FILE SPECS
	//
	CString strFileSpec;
			strFileSpec.LoadString( IDS_BUILDFILES );

	//
	//	LET USER SELECT BUILDS
	//
	CFileDialog dlgBrowser( FALSE, "", "", OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, strFileSpec );

	if ( IDOK != dlgBrowser.DoModal() )
	{
		return;
	}

	//
	//	EXPORT
	//
	this->ExportBuilds( dlgBrowser.GetPathName() );
}

//***************************************************************************************************
//**																			 OnClickWorkspaceList
//***************************************************************************************************
//**	@DOC		MESSAGES
//**	@MFUNC		Called when the user clicks inside the workspace list control
//**	@COMM		The method assures that when a selected item is (un-)checked all other selected 
//**				items will have the same check state, too.
//**	@DEVNOTE	Double clicking does not affect other selected items. 
//**				This is not a bug but rather a feature that comes handy if you want to change 
//**				a selected item's check state without losing or modifying your selection.
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::OnClickWorkspaceList( NMHDR* pNMHDR, LRESULT* pResult) 
{
	//
	//	GET LIST VIEW NOTIFICATION HEADER
	//
	NMLISTVIEW* pNMListView = reinterpret_cast< NMLISTVIEW* > (pNMHDR);

	//
	//	WE'RE ONLY INTERESTED IN CLICKS ON A SELECTED ITEM'S CHECK BOX
	//
	if ( pNMListView->ptAction.x >= 16 )	
	{
		return;								// not the check box [@HACK: assumes checkboxes are always 16 pixels wide]
	}

	if ( LVIS_SELECTED != this->m_ctrlListWorkspaces.GetItemState( pNMListView->iItem, LVIS_SELECTED ) )
	{
		return;								// not selected
	}
	
	//
	//	GET THE ITEM's CHECK STATE
	//
	BOOL bChecked = this->m_ctrlListWorkspaces.GetCheck( pNMListView->iItem );

	//
	//	SET INVERSE CHECK STATE FOR ALL SELECTED ITEMS EXCEPT THIS ONE
	//
	POSITION pos = this->m_ctrlListWorkspaces.GetFirstSelectedItemPosition();

	while( NULL != pos )
	{
		//
		//	GET NEXT SELECTED ITEM'S INDEX
		//
		int iItem = this->m_ctrlListWorkspaces.GetNextSelectedItem( pos );

		//
		//	CHECK FOR CURRENT ITEM
		//
		if ( iItem == pNMListView->iItem )
		{
			continue;
		}

		//
		//	SET INVERSE CHECK
		//
		this->m_ctrlListWorkspaces.SetCheck( iItem, bChecked ? FALSE : TRUE );
	}

	//
	//	RETURN RESULT
	//
	*pResult = 0;
}

//
//---------------------------------------------------------------------------------------------------
//*****************************************     HELPERS     *****************************************
//---------------------------------------------------------------------------------------------------
//

//***************************************************************************************************
//**																					 AddWorkspace
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Adds all projects of the specified workspace to the project list
//**	@PARM		[in] The workspace's path
//**	@PARM		[in] The workspace's name
//**	@RDESC		<t TRUE> if successful, <t FALSE> otherwise.
//**	@END
//***************************************************************************************************
//inline
BOOL CDialogProjects::AddWorkspace( const CString &a_strWorkspacePath, const CString &a_strWorkspaceName )
{
	//
	//	CHECK FOR VALID APPLICATION INTERFACE
	//
	if ( NULL == this->m_pIApplication )
	{
		this->MessageBox( "Application interface not intitialized!", "CDialogProjects::AddWorkspace()", MB_ICONERROR );
		return FALSE;
	}

	//
	//	SETUP VARS
	//
	CComPtr< IDispatch >		pIDispDocuments		;
	CComPtr< IDispatch >		pIDispWorkspace		;  
	CComPtr< IDispatch >		pIDispProjects		;
	CComPtr< IDispatch >		pIDispProject		;
	CComBSTR					bszWorkspacePathName( a_strWorkspacePath );
	CComBSTR					bszProjectName		;
	CComBSTR					bszProjectPath		;
	CComBSTR					bszConfigurationName;
	CString						strProjectPath		;
	CString						strProjectName		;
	CString						strConfiguration	;
	CString						strConfigurations	;
	CComVariant					varEmpty			; 
	CComVariant					varProject			; 
	CComVariant					varConfiguration	; 
	long						lProjects			= 0;
	long						lConfigurations		= 0;

	//
	//	GET DOCUMENTS
	//
	VERIFY_OK( this->m_pIApplication->get_Documents( &pIDispDocuments ) );

	//
	//	LOAD WORKSPACE
	//
	CComQIPtr< IDocuments, &IID_IDocuments > pIDocuments( pIDispDocuments );

	VERIFY_OK( pIDocuments->Open( bszWorkspacePathName, varEmpty, varEmpty, &pIDispWorkspace ) );

	//
	//	GET PROJECTS
	//
	VERIFY_OK( this->m_pIApplication->get_Projects( &pIDispProjects ) );

	CComQIPtr< IProjects, &IID_IProjects > pIProjects( pIDispProjects );

	//
	//	ENUMERATE PROJECTS
	//
	VERIFY_OK( pIProjects->get_Count( &lProjects ) );

	for ( long lProject = 1 ; lProject < lProjects + 1 ; lProject++ )
	{
		//
		//	RESET CONFIGURATIONS
		//
		strConfigurations ="";

		//
		//	GET NEXT GENERIC PROJECT
		//
		varProject = lProject;

		CComPtr< IGenericProject > pIGenericProject;	

		VERIFY_OK( pIProjects->Item( varProject, &pIGenericProject ) );

		//
		//	QUERY BUILD PROJECT
		//
		CComQIPtr< IBuildProject, &IID_IBuildProject > pIProject( pIGenericProject );

		//
		//	GET PATH AND NAME
		//
		VERIFY_OK( pIProject->get_FullName	( &bszProjectPath ) );
		VERIFY_OK( pIProject->get_Name		( &bszProjectName ) );

		strProjectPath = bszProjectPath;
		strProjectName = bszProjectName;

		//
		//	GET CONFIGURATIONS
		//
		CComPtr< IConfigurations > pIConfigurations;

		VERIFY_OK( pIProject->get_Configurations( &pIConfigurations ) );

		//
		//	ENUMERATE CONFIGURATIONS	
		//
		VERIFY_OK( pIConfigurations->get_Count( &lConfigurations ) );

		for ( long lConfiguration = 1 ; lConfiguration < lConfigurations + 1 ; lConfiguration++ )
		{
			//
			//	GET NEXT CONFIGURATION
			//
			CComPtr< IConfiguration	> pIConfiguration;

			varConfiguration = lConfiguration;

			VERIFY_OK( pIConfigurations->Item( varConfiguration, &pIConfiguration ) );

			//
			//	GET NAME
			//
			VERIFY_OK( pIConfiguration->get_Name( &bszConfigurationName ) );

			strConfiguration = bszConfigurationName;

			//
			//	REMOVE PROJECT NAME PREFIX
			//
			int iPos = strConfiguration.Find( strProjectName + " - " );

			if ( 0 == iPos )
			{
				strConfiguration.Delete( 0, strProjectName.GetLength() + 3 );
			}

			//
			//	ADD TO CONFIGURATIONS
			//
			strConfigurations += strConfiguration;

			if ( lConfiguration != lConfigurations )
			{
				strConfigurations += ";";
			}
		}

		//
		//	ADD TO LIST
		//
		this->AddProject( a_strWorkspacePath, a_strWorkspaceName, strProjectPath, strProjectName, strConfigurations );
	}

	//
	//	RETURN SUCCESS
	//
	return TRUE;
}

//***************************************************************************************************
//**																					   AddProject
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Adds the pseicifed project to the list control
//**	@PARM		[in] The workspace	's path
//**	@PARM		[in] The workspace	's name
//**	@PARM		[in] The project	's path
//**	@PARM		[in] The project	's name
//**	@PARM		[in] The project	's configurations
//**	@PARM		[in] The project	's style
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::AddProject( const CString &a_strWorkspacePath, const CString &a_strWorkspaceName, const CString &a_strProjectPath, const CString &a_strProjectName, const CString &a_strConfigurations, int a_iStyle /*= 0x01*/ )
{
	//
	//	GET TEMP STRINGS
	//
	CString strWorkspacePath	( a_strWorkspacePath  );
	CString strWorkspaceName	( a_strWorkspaceName  );
	CString strProjectPath		( a_strProjectPath	  );
	CString strProjectName		( a_strProjectName	  );
	CString	strConfigurations	( a_strConfigurations );

	//
	//	SETUP ITEM
	//
	LVITEM item;

	item.mask		= LVIF_TEXT | LVIF_STATE;
	item.iItem		= this->m_ctrlListWorkspaces.GetItemCount();
	item.iSubItem	= 0;
	item.lParam		= 0;
	item.state		= 0;
	item.pszText	= strProjectName.GetBuffer( _MAX_PATH );

	//
	//	INSET ITEM
	//
	item.iItem		= this->m_ctrlListWorkspaces.InsertItem( &item );

	//
	//	SET SUBITEMS
	//
	item.iSubItem	= 1;
	item.pszText	= strWorkspaceName.GetBuffer( _MAX_PATH );

					this->m_ctrlListWorkspaces.SetItem(  &item );

	item.iSubItem	= 2;
	item.pszText	= strProjectPath.GetBuffer( _MAX_PATH );

					this->m_ctrlListWorkspaces.SetItem(  &item );

	item.iSubItem	= 3;
	item.pszText	= strWorkspacePath.GetBuffer( _MAX_PATH );

					this->m_ctrlListWorkspaces.SetItem(  &item );

	item.iSubItem	= 4;
	item.pszText	= strConfigurations.GetBuffer( _MAX_PATH );

					this->m_ctrlListWorkspaces.SetItem(  &item );

	//
	//	RELEASE BUFFERS
	//
	strWorkspacePath	.ReleaseBuffer();
	strWorkspaceName	.ReleaseBuffer();
	strProjectPath		.ReleaseBuffer();
	strProjectName		.ReleaseBuffer();
	strConfigurations	.ReleaseBuffer();

	//
	//	CHECK ITEM
	//
	this->m_ctrlListWorkspaces.SetCheck( item.iItem, (a_iStyle & 0x01) );
}

//***************************************************************************************************
//**																					   GetProject
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Determines the settings of the project at the specified index
//**	@PARM		[in]  The desired project's index
//**	@PARM		[out] The workspace	's path
//**	@PARM		[out] The workspace	's name
//**	@PARM		[out] The project	's path
//**	@PARM		[out] The project	's name
//**	@PARM		[out] The project	's configurations
//**	@PARM		[out] The project	's style
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::GetProject( int a_iProject, CString &a_strWorkspacePath, CString &a_strWorkspaceName, CString &a_strProjectPath, CString &a_strProjectName, CString &a_strConfigurations, int& a_iStyle ) const
{
	//
	//	CHECK VALIDITY
	//
	if ( a_iProject < 0 )
	{
		return;
	}

	if ( a_iProject >= this->m_ctrlListWorkspaces.GetItemCount() )
	{
		return;
	}

	//
	//	GET ITEM TEXTs
	//
	this->m_ctrlListWorkspaces.GetItemText( a_iProject, 0, a_strProjectName		.GetBuffer( _MAX_PATH ), _MAX_PATH );
	this->m_ctrlListWorkspaces.GetItemText( a_iProject, 1, a_strWorkspaceName	.GetBuffer( _MAX_PATH ), _MAX_PATH );
	this->m_ctrlListWorkspaces.GetItemText( a_iProject, 2, a_strProjectPath		.GetBuffer( _MAX_PATH ), _MAX_PATH );
	this->m_ctrlListWorkspaces.GetItemText( a_iProject, 3, a_strWorkspacePath	.GetBuffer( _MAX_PATH ), _MAX_PATH );
	this->m_ctrlListWorkspaces.GetItemText( a_iProject, 4, a_strConfigurations	.GetBuffer( _MAX_PATH ), _MAX_PATH );

	a_strProjectName	.ReleaseBuffer();
	a_strWorkspaceName	.ReleaseBuffer();
	a_strProjectPath	.ReleaseBuffer();
	a_strWorkspacePath	.ReleaseBuffer();
	a_strConfigurations	.ReleaseBuffer();
	
	//
	//	GET CHECK STATE
	//
	BOOL bChecked = this->m_ctrlListWorkspaces.GetCheck( a_iProject );

	a_iStyle = 0;
	a_iStyle |= (bChecked) ? 0x01 : 0x00;
}

//***************************************************************************************************
//**																						 MoveItem
//***************************************************************************************************
//**	@DOC		HELPERS
//**	@MFUNC		Swaps the contents of the items at the specified indices
//**	@PARM		[in] The source index
//**	@PARM		[in] The target index
//**	@END
//***************************************************************************************************
//inline
void CDialogProjects::MoveItem( int a_iSrc, int a_iDst )
{
	//
	//	CHECK VALIDITY
	//
	if ( a_iSrc <  0										 )	return;
	if ( a_iDst <  0										 )	return;
	if ( a_iSrc >= this->m_ctrlListWorkspaces.GetItemCount() )	return;
	if ( a_iDst >= this->m_ctrlListWorkspaces.GetItemCount() )	return;

	//
	//	SETUP VARS
	//
	CString	strSwap	 ;
	CString	strTemp	 ;
	int		iSubItem = 0;

	//
	//	SWAP TEXTs
	//
	for ( iSubItem = 0; iSubItem < IDS_COL_PRJ_MAX - IDS_COL_PRJ_MIN; iSubItem++ )
	{
		//
		//	STORE DESTINATION IN SWAP MEM
		//
		strSwap = this->m_ctrlListWorkspaces.GetItemText( a_iDst, iSubItem );

		//
		//	COPY SOURCE TO DESTINATION
		//
		strTemp = this->m_ctrlListWorkspaces.GetItemText( a_iSrc, iSubItem );
				  this->m_ctrlListWorkspaces.SetItemText( a_iDst, iSubItem, strTemp.GetBuffer( _MAX_PATH ) );

		strTemp.ReleaseBuffer();

		//
		//	COPY SWAP TO SOURCE
		//
		this->m_ctrlListWorkspaces.SetItemText( a_iSrc, iSubItem, strSwap.GetBuffer( _MAX_PATH ) );

		strSwap.ReleaseBuffer();
	}

	//
	//	SWAP ITEM STATES
	//
	UINT uiSrc = this->m_ctrlListWorkspaces.GetItemState( a_iSrc, (UINT) -1 );
	UINT uiDst = this->m_ctrlListWorkspaces.GetItemState( a_iDst, (UINT) -1 );

	this->m_ctrlListWorkspaces.SetItemState( a_iSrc, uiDst, (UINT) -1 );
	this->m_ctrlListWorkspaces.SetItemState( a_iDst, uiSrc, (UINT) -1 );
}
